package com.ctrip.platform.dal.dao;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public interface ResultMerger<T> {
    void addPartial(String shard, T partial) throws SQLException;

    T merge() throws SQLException;

    static class IntSummary implements ResultMerger<Integer> {
        private int total;

        @Override
        public void addPartial(String shard, Integer partial) {
            total += partial.intValue();
        }

        @Override
        public Integer merge() {
            return total;
        }
    }

    static class LongSummary implements ResultMerger<Long> {
        private long total;

        @Override
        public void addPartial(String shard, Long partial) {
            total += partial.longValue();
        }

        @Override
        public Long merge() {
            return total;
        }
    }

    static class LongNumberSummary implements ResultMerger<Number> {
        private long total;

        @Override
        public void addPartial(String shard, Number partial) {
            total += partial.longValue();
        }

        @Override
        public Long merge() {
            return total;
        }
    }

    static class DoubleSummary implements ResultMerger<Double> {
        private double total;

        @Override
        public void addPartial(String shard, Double partial) {
            total += partial.doubleValue();
        }

        @Override
        public Double merge() {
            return total;
        }
    }

    static class BigIntegerSummary implements ResultMerger<BigInteger> {
        private BigInteger total;

        @Override
        public void addPartial(String shard, BigInteger partial) {
            if (total == null)
                total = partial;
            else
                total.add(partial);
        }

        @Override
        public BigInteger merge() {
            return total;
        }
    }

    static class BigDecimalSummary implements ResultMerger<BigDecimal> {
        private BigDecimal total;

        @Override
        public void addPartial(String shard, BigDecimal partial) {
            if (total == null)
                total = partial;
            else
                total.add(partial);
        }

        @Override
        public BigDecimal merge() {
            return total;
        }
    }

    static class IntAverage implements ResultMerger<Map<String, Number>> {
        private int count;
        private int sum;
        private String countColumn;
        private String sumColumn;
        private String averageColumn;

        public IntAverage() {
            this("count", "sum", "average");
        }

        public IntAverage(String countColumn, String sumColumn, String averageColumn) {
            this.countColumn = countColumn;
            this.sumColumn = sumColumn;
            this.averageColumn = averageColumn;
        }

        @Override
        public void addPartial(String shard, Map<String, Number> partial) {
            count += partial.get(countColumn).intValue();
            sum += partial.get(sumColumn).intValue();
        }

        @Override
        public Map<String, Number> merge() {
            Map<String, Number> result = new HashMap<>();
            result.put(countColumn, count);
            result.put(sumColumn, sum);
            result.put(averageColumn, sum / count);

            return result;
        }
    }

    static class LongAverage implements ResultMerger<Map<String, Number>> {
        private int count;
        private long sum;
        private String countColumn;
        private String sumColumn;
        private String averageColumn;

        public LongAverage() {
            this("count", "sum", "average");
        }

        public LongAverage(String countColumn, String sumColumn, String averageColumn) {
            this.countColumn = countColumn;
            this.sumColumn = sumColumn;
            this.averageColumn = averageColumn;
        }

        @Override
        public void addPartial(String shard, Map<String, Number> partial) {
            count += partial.get(countColumn).intValue();
            sum += partial.get(sumColumn).longValue();
        }

        @Override
        public Map<String, Number> merge() {
            Map<String, Number> result = new HashMap<>();
            result.put(countColumn, count);
            result.put(sumColumn, sum);
            result.put(averageColumn, sum / count);

            return result;
        }
    }

    static class DoubleAverage implements ResultMerger<Map<String, Number>> {
        private int count;
        private double sum;
        private String countColumn;
        private String sumColumn;
        private String averageColumn;

        public DoubleAverage() {
            this("count", "sum", "average");
        }

        public DoubleAverage(String countColumn, String sumColumn, String averageColumn) {
            this.countColumn = countColumn;
            this.sumColumn = sumColumn;
            this.averageColumn = averageColumn;
        }

        @Override
        public void addPartial(String shard, Map<String, Number> partial) {
            count += partial.get(countColumn).intValue();
            sum += partial.get(sumColumn).doubleValue();
        }

        @Override
        public Map<String, Number> merge() {
            Map<String, Number> result = new HashMap<>();
            result.put(countColumn, count);
            result.put(sumColumn, sum);
            result.put(averageColumn, sum / count);

            return result;
        }
    }

    static class BigIntegerAverage implements ResultMerger<Map<String, Number>> {
        private int count;
        private BigInteger sum;
        private String countColumn;
        private String sumColumn;
        private String averageColumn;

        public BigIntegerAverage() {
            this("count", "sum", "average");
        }

        public BigIntegerAverage(String countColumn, String sumColumn, String averageColumn) {
            this.countColumn = countColumn;
            this.sumColumn = sumColumn;
            this.averageColumn = averageColumn;
        }

        @Override
        public void addPartial(String shard, Map<String, Number> partial) {
            count += partial.get(countColumn).intValue();
            if (sum == null)
                sum = (BigInteger) partial.get(sumColumn);
            else
                sum.add((BigInteger) partial.get(sumColumn));
        }

        @Override
        public Map<String, Number> merge() {
            Map<String, Number> result = new HashMap<>();
            result.put(countColumn, count);
            result.put(sumColumn, sum);
            result.put(averageColumn, sum.divide(new BigInteger(String.valueOf(count))));

            return result;
        }
    }

    static class BigDecimalAverage implements ResultMerger<Map<String, Number>> {
        private int count;
        private BigDecimal sum;
        private String countColumn;
        private String sumColumn;
        private String averageColumn;

        public BigDecimalAverage() {
            this("count", "sum", "average");
        }

        public BigDecimalAverage(String countColumn, String sumColumn, String averageColumn) {
            this.countColumn = countColumn;
            this.sumColumn = sumColumn;
            this.averageColumn = averageColumn;
        }

        @Override
        public void addPartial(String shard, Map<String, Number> partial) {
            count += partial.get(countColumn).intValue();
            if (sum == null)
                sum = (BigDecimal) partial.get(sumColumn);
            else
                sum.add((BigDecimal) partial.get(sumColumn));
        }

        @Override
        public Map<String, Number> merge() {
            Map<String, Number> result = new HashMap<>();
            result.put(countColumn, count);
            result.put(sumColumn, sum);
            result.put(averageColumn, sum.divide(new BigDecimal(String.valueOf(count))));

            return result;
        }
    }

}
